import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { networkInterfaces } from 'os';
import { Context as VmContext } from 'vm';

import type { RequestBodyType } from './types';
import type { ValidationError } from 'class-validator';
import type { Context } from 'koa';

/**
 * @func getDirname
 * @desc 获取当前模块的目录名
 * @param moduleUrl 当前模块的URL
 * @example
 * getDirname(import.meta.url)
 */
export const getDirname = (moduleUrl: string) => {
  const _filename = fileURLToPath(moduleUrl);
  return dirname(_filename);
};

/**
 * @func getIpAddress
 * @desc 获取本机IP地址
 */
export const getIpAddress = () => {
  const networkInterfaceMap = networkInterfaces();

  for (const key in networkInterfaceMap) {
    const networkInterfaceInfoList = networkInterfaceMap[key];

    if (networkInterfaceInfoList) {
      for (const alias of networkInterfaceInfoList) {
        if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
          return alias.address;
        }
      }
    }
  }
};

/**
 * @func getClientIpAddress
 * @desc 获取客户端IP地址
 * @param ctx Koa上下文
 * @return string 如果获取失败，返回'0.0.0.0'，否则返回客户端IP地址
 */
export const getClientIpAddress = (ctx: VmContext) => {
  const headers = ctx.headers;

  if (headers['x-forwarded-for']) {
    const ipList = headers['x-forwarded-for'].split(',');

    return ipList[0];
  }

  return '0.0.0.0';
};

/**
 * 根据请求类型获取验证目标
 * 该函数用于根据传入的请求类型，从上下文对象中选择并返回相应的请求数据部分
 * 它支持三种请求数据类型：body（请求体）、query（查询参数）、params（路由参数）
 * @param ctx koa上下文对象，包含了请求的相关信息
 * @param type 请求体类型，决定了从上下文对象中获取哪个部分的数据
 * @returns 返回一个对象，包含根据请求类型获取的数据
 */
export const getValidateTarget = (ctx: Context, type: RequestBodyType): Record<string, any> => {
  switch (type) {
    case 'body':
      // 返回请求体数据
      return ctx.request.body;
    case 'query':
      // 返回查询参数数据
      return ctx.request.query;
    default:
      // 返回路由参数数据
      return ctx.params;
  }
};

/**
 * @func handleExceptions
 * @desc 处理class-validator校验时抛出的错误
 * @param error
 */
export const handleExceptions = (error: any) => {
  let errMsg = '';
  if (Array.isArray(error)) {
    (error as ValidationError[]).forEach((item) => {
      if (!Typing.objIsEmpty(item.constraints)) {
        const keys = Object.keys(item.constraints as Record<string, any>);
        errMsg += (keys as string[]).reduce((pre, cur) => {
          return pre + item.constraints![cur] + ';';
        }, '');
      }
    });
  } else {
    errMsg += error.message;
  }
  return errMsg;
};

/**
 * @class Typing
 * @desc 类型判断工具
 */
export class Typing {
  // 判断是否为null
  static isNull(value: any) {
    return value === null;
  }

  // 判断是否为undefined
  static isUndef(value: any) {
    return value === undefined;
  }

  /**
   * @func isEmpty
   * @desc 判断对象是否为空，包括null, undefined, {}
   * @param value
   */
  static objIsEmpty(value: any) {
    if (value === null || value === undefined) return true;
    return !Object.keys(value).length;
  }

  // 获取详细类型
  static getType(value: any) {
    if (typeof value === 'number' && isNaN(value)) return 'NaN';
    const res = Object.prototype.toString.call(value);
    return res.substring(8, res.length - 1);
  }
}
